package com.me.study.concurrent.thread;

import com.me.common.util.SleepUtil;

/**
 * 控制线程执行顺序的方式：join() 原理
 *
 * @author ME
 * @date   2019/7/4
 */
public class ThreadExecutionOrder {
    public static void main(String[] args) throws InterruptedException {
        // 使用 join()
        // System.out.println("threadJoin:");
        // threadJoin();

        // 其实是模仿了 join() 实现
        System.out.println("threadWait:");
        new ThreadExecutionOrder().threadWait();
    }

    /**
     * <pre> 方法引用仅需要关心 返回值/入参,无需关心修饰符,如:
     * 原方法为公开的、抽象的, 实际引用了私有的、静态的 </pre>
     *
     * 需要的方法为: <p>
     * [Runnable] public abstract void run();<p>
     * 实际引用: <p>
     * [ThreadExecutionOrder] private static void action()
     */
    public void threadWait() throws InterruptedException {
        Thread t1 = new Thread(ThreadExecutionOrder::action, "t1");
        Thread t2 = new Thread(ThreadExecutionOrder::action, "t2");
        Thread t3 = new Thread(ThreadExecutionOrder::action, "t3");

        Object o = new Object();
        threadStartAndWait(t1, o);
        threadStartAndWait(t2, o);
        threadStartAndWait(t3, o);
    }

    public static void threadStartAndWait(Thread t, Object o) throws InterruptedException {
        if (Thread.State.NEW.equals(t.getState())) {
            t.start();
        }

        while (t.isAlive()) {
            synchronized (t) {
                t.wait();
            }

            // TODO 替换成其他锁对象（如：Object o）: t线程 执行完成后, main线程 依旧是 WAIT状态.
            //  说明了两个结果:
            //  1, wait() 阻塞的是调用此方法的当前线程.
            //  2, t线程 执行完会自动调用 自己的 notifyAll() 唤醒所有等待对象锁（自己）的线程
            //      (由于 main线程 等待的是 o的锁, 所以 t线程 执行之后无法唤醒 main)
            // synchronized (o) {
            //     o.wait();
            // }
        }
    }

    private static void threadJoin() throws InterruptedException {
        Thread t1 = new Thread(ThreadExecutionOrder::action, "t1");
        t1.start();
        // 阻塞 main线程，等待 t1 完成后唤醒 main
        t1.join();
        Thread t2 = new Thread(ThreadExecutionOrder::action, "t2");
        t2.start();
        t2.join();
        Thread t3 = new Thread(ThreadExecutionOrder::action, "t3");
        t3.start();
        t3.join();
    }

    private static void action() {
        SleepUtil.seconds(3);
        System.out.printf("线程[%s] 正在执行...\n", Thread.currentThread().getName());
    }
}
